/*-------------------------------------------------------------------------
 *
 * jsonlog.c
 *	  JSON日志记录
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/error/jsonlog.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/xact.h"
#include "libpq/libpq.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "postmaster/bgworker.h"
#include "postmaster/syslogger.h"
#include "storage/lock.h"
#include "storage/proc.h"
#include "tcop/tcopprot.h"
#include "utils/backend_status.h"
#include "utils/elog.h"
#include "utils/guc.h"
#include "utils/json.h"
#include "utils/ps_status.h"

static void fc_appendJSONKeyValueFmt(StringInfo fc_buf, const char *fc_key,
								  bool fc_escape_key,
								  const char *fc_fmt,...) pg_attribute_printf(4, 5);

/*
 * appendJSONKeyValue
 *
 * 将逗号后跟 JSON 键和值附加到 StringInfo 中。
 * 键始终被转义。值可选择性地被转义，这取决于键的数据类型。
 */
static void fc_appendJSONKeyValue(StringInfo fc_buf, const char *fc_key, const char *fc_value,
				   bool fc_escape_value)
{
	Assert(fc_key != NULL);

	if (fc_value == NULL)
		return;

	appendStringInfoChar(fc_buf, ',');
	escape_json(fc_buf, fc_key);
	appendStringInfoChar(fc_buf, ':');

	if (fc_escape_value)
		escape_json(fc_buf, fc_value);
	else
		appendStringInfoString(fc_buf, fc_value);
}


/*
 * appendJSONKeyValueFmt
 *
 * 评估 fmt 字符串，然后将 appendJSONKeyValue() 作为 JSON 属性的值调用。
 * 键和值都将由 appendJSONKeyValue() 转义。
 */
static void fc_appendJSONKeyValueFmt(StringInfo fc_buf, const char *fc_key,
					  bool fc_escape_key, const char *fc_fmt,...)
{
	int			fc_save_errno = errno;
	size_t		fc_len = 128;		/* 关于缓冲区大小的初始假设 */
	char	   *fc_value;

	for (;;)
	{
		va_list		fc_args;
		size_t		fc_newlen;

		/* 分配结果缓冲区 */
		fc_value = (char *) palloc(fc_len);

		/* 尝试格式化数据。 */
		errno = fc_save_errno;
		va_start(fc_args, fc_fmt);
		fc_newlen = pvsnprintf(fc_value, fc_len, fc_fmt, fc_args);
		va_end(fc_args);

		if (fc_newlen < fc_len)
			break;				/* 成功 */

		/* 释放缓冲区并循环以尝试更大的 len。 */
		pfree(fc_value);
		fc_len = fc_newlen;
	}

	fc_appendJSONKeyValue(fc_buf, fc_key, fc_value, fc_escape_key);

	/* 清理 */
	pfree(fc_value);
}

/*
 * 以 json 格式写入日志。
 */
void write_jsonlog(ErrorData *fc_edata)
{
	StringInfoData fc_buf;
	char	   *fc_start_time;
	char	   *fc_log_time;

	/* 行号的静态计数器 */
	static long fc_log_line_number = 0;

	/* 计数器是否在当前进程中被重置？ */
	static int	fc_log_my_pid = 0;

	/*
	 * 这是少数几个我们宁愿不从postmaster那里继承静态
	 * 变量值的地方之一。但由于我们会这样做，所以在
	 * MyProcPid更改时重置它。
	 */
	if (fc_log_my_pid != MyProcPid)
	{
		fc_log_line_number = 0;
		fc_log_my_pid = MyProcPid;
		reset_formatted_start_time();
	}
	fc_log_line_number++;

	initStringInfo(&fc_buf);

	/* 初始化字符串 */
	appendStringInfoChar(&fc_buf, '{');

	/* 带毫秒的时间戳 */
	fc_log_time = get_formatted_log_time();

	/*
	 * 第一个属性不使用 appendJSONKeyValue，因为它没有
	 * 逗号前缀。
	 */
	escape_json(&fc_buf, "timestamp");
	appendStringInfoChar(&fc_buf, ':');
	escape_json(&fc_buf, fc_log_time);

	/* 用户名 */
	if (MyProcPort)
		fc_appendJSONKeyValue(&fc_buf, "user", MyProcPort->user_name, true);

	/* 数据库名称 */
	if (MyProcPort)
		fc_appendJSONKeyValue(&fc_buf, "dbname", MyProcPort->database_name, true);

	/* 进程 ID */
	if (MyProcPid != 0)
		fc_appendJSONKeyValueFmt(&fc_buf, "pid", false, "%d", MyProcPid);

	/* 远程主机和端口 */
	if (MyProcPort && MyProcPort->remote_host)
	{
		fc_appendJSONKeyValue(&fc_buf, "remote_host", MyProcPort->remote_host, true);
		if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
			fc_appendJSONKeyValue(&fc_buf, "remote_port", MyProcPort->remote_port, false);
	}

	/* 会话 ID */
	fc_appendJSONKeyValueFmt(&fc_buf, "session_id", true, "%" INT64_MODIFIER "x.%x",
						  MyStartTime, MyProcPid);

	/* 行号 */
	fc_appendJSONKeyValueFmt(&fc_buf, "line_num", false, "%ld", fc_log_line_number);

	/* PS显示 */
	if (MyProcPort)
	{
		StringInfoData fc_msgbuf;
		const char *fc_psdisp;
		int			fc_displen;

		initStringInfo(&fc_msgbuf);
		fc_psdisp = get_ps_display(&fc_displen);
		appendBinaryStringInfo(&fc_msgbuf, fc_psdisp, fc_displen);
		fc_appendJSONKeyValue(&fc_buf, "ps", fc_msgbuf.data, true);

		pfree(fc_msgbuf.data);
	}

	/* 会话开始时间戳 */
	fc_start_time = get_formatted_start_time();
	fc_appendJSONKeyValue(&fc_buf, "session_start", fc_start_time, true);

	/* 虚拟事务ID */
	/* 保持VXID格式与lockfuncs.c同步 */
	if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
		fc_appendJSONKeyValueFmt(&fc_buf, "vxid", true, "%d/%u", MyProc->backendId,
							  MyProc->lxid);

	/* 事务ID */
	fc_appendJSONKeyValueFmt(&fc_buf, "txid", false, "%u",
						  GetTopTransactionIdIfAny());

	/* 错误严重性 */
	if (fc_edata->elevel)
		fc_appendJSONKeyValue(&fc_buf, "error_severity",
						   (char *) error_severity(fc_edata->elevel), true);

	/* SQL状态代码 */
	if (fc_edata->sqlerrcode)
		fc_appendJSONKeyValue(&fc_buf, "state_code",
						   unpack_sql_state(fc_edata->sqlerrcode), true);

	/* 错误消息 */
	fc_appendJSONKeyValue(&fc_buf, "message", fc_edata->message, true);

	/* 错误详细或 error_detail 日志 */
	if (fc_edata->detail_log)
		fc_appendJSONKeyValue(&fc_buf, "detail", fc_edata->detail_log, true);
	else
		fc_appendJSONKeyValue(&fc_buf, "detail", fc_edata->detail, true);

	/* 错误提示 */
	if (fc_edata->hint)
		fc_appendJSONKeyValue(&fc_buf, "hint", fc_edata->hint, true);

	/* 内部查询 */
	if (fc_edata->internalquery)
		fc_appendJSONKeyValue(&fc_buf, "internal_query", fc_edata->internalquery,
						   true);

	/* 如果打印了内部查询，也打印内部位置 */
	if (fc_edata->internalpos > 0 && fc_edata->internalquery != NULL)
		fc_appendJSONKeyValueFmt(&fc_buf, "internal_position", false, "%d",
							  fc_edata->internalpos);

	/* 错误上下文 */
	if (fc_edata->context && !fc_edata->hide_ctx)
		fc_appendJSONKeyValue(&fc_buf, "context", fc_edata->context, true);

	/* 用户查询 --- 仅在未被调用者禁用时报告 */
	if (check_log_of_query(fc_edata))
	{
		fc_appendJSONKeyValue(&fc_buf, "statement", debug_query_string, true);
		if (fc_edata->cursorpos > 0)
			fc_appendJSONKeyValueFmt(&fc_buf, "cursor_position", false, "%d",
								  fc_edata->cursorpos);
	}

	/* 文件错误位置 */
	if (Log_error_verbosity >= PGERROR_VERBOSE)
	{
		if (fc_edata->funcname)
			fc_appendJSONKeyValue(&fc_buf, "func_name", fc_edata->funcname, true);
		if (fc_edata->filename)
		{
			fc_appendJSONKeyValue(&fc_buf, "file_name", fc_edata->filename, true);
			fc_appendJSONKeyValueFmt(&fc_buf, "file_line_num", false, "%d",
								  fc_edata->lineno);
		}
	}

	/* 应用程序名称 */
	if (application_name && application_name[0] != '\0')
		fc_appendJSONKeyValue(&fc_buf, "application_name", application_name, true);

	/* 后端类型 */
	fc_appendJSONKeyValue(&fc_buf, "backend_type", get_backend_type_for_log(), true);

	/* 领导进程ID */
	if (MyProc)
	{
		PGPROC	   *fc_leader = MyProc->lockGroupLeader;

		/*
		 * 仅为活动的并行工作线程显示领导者。这将不包括
		 * 并行组的领导者。
		 */
		if (fc_leader && fc_leader->pid != MyProcPid)
			fc_appendJSONKeyValueFmt(&fc_buf, "leader_pid", false, "%d",
								  fc_leader->pid);
	}

	/* 查询ID */
	fc_appendJSONKeyValueFmt(&fc_buf, "query_id", false, "%lld",
						  (long long) pgstat_get_my_query_id());

	/* 完成字符串 */
	appendStringInfoChar(&fc_buf, '}');
	appendStringInfoChar(&fc_buf, '\n');

	/* 如果在syslogger进程中，尝试直接写入文件的消息 */
	if (MyBackendType == B_LOGGER)
		write_syslogger_file(fc_buf.data, fc_buf.len, LOG_DESTINATION_JSONLOG);
	else
		write_pipe_chunks(fc_buf.data, fc_buf.len, LOG_DESTINATION_JSONLOG);

	pfree(fc_buf.data);
}
